﻿using System.Linq;
using ex = System.Linq.Expressions;

// Author: Miroslav Matula m.matula@alutam.cz 
// Date : 9.9.2010

namespace System.Windows.Controls
{
  public abstract class FilterDescriptorOData
  {
    public static ex.Expression ConvertToExpression(Type entityType_, FilterDescriptorOData filter_)
    {
      ex.ParameterExpression ex_parameter = ex.Expression.Parameter(entityType_, "p");
      var exp = ConvertToExpression_(entityType_, filter_, ref ex_parameter);
      return ex.Expression.Lambda(exp, ex_parameter);
    }

    private static ex.Expression ConvertToExpression_(Type entityType_, FilterDescriptorOData filter_, ref ex.ParameterExpression ex_parameter_)
    {
      var type = entityType_;

      if(filter_ is FilterDescriptorExpressionOData) {
        //-- expression +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        FilterDescriptorExpressionOData f_ex = filter_ as FilterDescriptorExpressionOData;
        //-- column
        var property = type.GetProperty(f_ex.PropertyName);
        if(property == null) throw new Exception("The filter name " + f_ex.PropertyName + " is invalid");
        var ex_propertyAccess = ex.Expression.MakeMemberAccess(ex_parameter_, property);//
        ex.Expression ex_object = null;
        if(!f_ex.IsCaseSensitive &&(property.PropertyType == typeof(string))) {
          ex_object = System.Linq.Expressions.Expression.Call(ex_propertyAccess, typeof(string).GetMethod("ToUpper",new Type[]{}));
        } else {
          ex_object = ex_propertyAccess;
        }
        //-- constanta --------
        // převod
        object comp_val = f_ex.Value;
        if(property.PropertyType == typeof(string) && !f_ex.IsCaseSensitive) comp_val = ((string)comp_val).ToUpper();
        if(property.PropertyType != typeof(string)) {
          try {
            if(property.PropertyType == typeof(Byte)) comp_val = Byte.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(sbyte)) comp_val = sbyte.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(Int16)) comp_val = Int16.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(Int32)) comp_val = Int32.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(Int64)) comp_val = Int64.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(UInt16)) comp_val = UInt16.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(UInt32)) comp_val = UInt32.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(UInt64)) comp_val = UInt64.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(decimal)) comp_val = decimal.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(Double)) comp_val = Double.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(Single)) comp_val = Single.Parse(f_ex.Value);
            else if(property.PropertyType == typeof(DateTime)) comp_val = DateTime.Parse(f_ex.Value);
          } catch {
            throw new Exception(string.Format("Can not convert {0} to type {1}", f_ex.Value, property.PropertyType.Name));
          }
        }
        ex.ConstantExpression ex_constant = ex.Expression.Constant(comp_val);
        //-- Expression -------
        ex.Expression ex_new = null;
        switch(f_ex.Operator) {
          case FilterDescriptorExpressionOperator.Contains:
          ex_new = System.Linq.Expressions.Expression.Call(ex_object, typeof(string).GetMethod("Contains", new[] { typeof(string) }), ex_constant);
          break;
          case FilterDescriptorExpressionOperator.EndsWith:
          ex_new = System.Linq.Expressions.Expression.Call(ex_object, typeof(String).GetMethod("EndsWith", new[] { typeof(string) }), ex_constant);
          break;
          case FilterDescriptorExpressionOperator.StartsWith:
          ex_new = System.Linq.Expressions.Expression.Call(ex_object, typeof(String).GetMethod("StartsWith", new[] { typeof(string) }), ex_constant);
          break;
          //case FilterOperator.IsContainedIn:
          //	xpr_new = System.Linq.Expressions.Expression.Call(ex_par, typeof(String).GetMethod("", new[] { typeof(string) }), ex_const);
          //break;
          case FilterDescriptorExpressionOperator.IsEqualTo:
          ex_new = System.Linq.Expressions.Expression.Equal(ex_object, ex_constant);
          break;
          case FilterDescriptorExpressionOperator.IsGreaterThan:
          if(property.PropertyType == typeof(string)) {
            ex_new = System.Linq.Expressions.Expression.GreaterThan(
              System.Linq.Expressions.Expression.Call(ex_object, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), ex_constant)
              , System.Linq.Expressions.Expression.Constant(0));
          } else {
            ex_new = System.Linq.Expressions.Expression.GreaterThan(ex_object, ex_constant);
          }
          break;
          case FilterDescriptorExpressionOperator.IsGreaterThanOrEqualTo:
          if(property.PropertyType == typeof(string)) {
            ex_new = System.Linq.Expressions.Expression.GreaterThanOrEqual(
              System.Linq.Expressions.Expression.Call(ex_object, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), ex_constant)
              , System.Linq.Expressions.Expression.Constant(0));
          } else {
            ex_new = System.Linq.Expressions.Expression.GreaterThanOrEqual(ex_object, ex_constant);
          }
          break;
          case FilterDescriptorExpressionOperator.IsLessThan:
          if(property.PropertyType == typeof(string)) {
            ex_constant = System.Linq.Expressions.Expression.Constant(comp_val + "ZZZZZZZZZZZZZZ");
            ex_new = System.Linq.Expressions.Expression.LessThan(
              System.Linq.Expressions.Expression.Call(ex_object, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), ex_constant)
              , System.Linq.Expressions.Expression.Constant(0));
          } else {
            ex_new = System.Linq.Expressions.Expression.LessThan(ex_object, ex_constant);
          }
          break;
          case FilterDescriptorExpressionOperator.IsLessThanOrEqualTo:
          if(property.PropertyType == typeof(string)) {
            ex_constant = System.Linq.Expressions.Expression.Constant(comp_val + "ZZZZZZZZZZZZZZ");
            ex_new = System.Linq.Expressions.Expression.LessThanOrEqual(
              System.Linq.Expressions.Expression.Call(ex_object, typeof(String).GetMethod("CompareTo", new[] { typeof(string) }), ex_constant)
              , System.Linq.Expressions.Expression.Constant(0));
          } else {
            ex_new = System.Linq.Expressions.Expression.LessThanOrEqual(ex_object, ex_constant);
          }
          break;
          case FilterDescriptorExpressionOperator.IsNotEqualTo:
          ex_new = System.Linq.Expressions.Expression.NotEqual(ex_object, ex_constant);
          break;
        }
        return ex_new;
      } else {
        // --  +++++++++++++++++++++++++++++++++++++++++
        FilterDescriptorLogicalOData f_log = filter_ as FilterDescriptorLogicalOData;
        if(f_log.Operator == FilterDescriptorLogicalOperator.And) {
          return (ex.Expression.AndAlso(ConvertToExpression_(entityType_, f_log.Left, ref ex_parameter_), ConvertToExpression_(entityType_, f_log.Right, ref ex_parameter_)));
        } if(f_log.Operator == FilterDescriptorLogicalOperator.Or) {
          return (ex.Expression.OrElse(ConvertToExpression_(entityType_, f_log.Left, ref ex_parameter_), ConvertToExpression_(entityType_, f_log.Right, ref ex_parameter_)));
        }
      }
      return null;
    }
  }


  public class FilterDescriptorLogicalOData : FilterDescriptorOData
  {
    public FilterDescriptorOData Left { get; set; }
    public FilterDescriptorOData Right { get; set; }
    public FilterDescriptorLogicalOperator Operator { get; set; }
  }

  public class FilterDescriptorExpressionOData : FilterDescriptorOData
  {
    public FilterDescriptorExpressionOData()
    {
      IsCaseSensitive = false;
    }
    //----------
    public string PropertyName { get; set; }
    //--------------
    public FilterDescriptorExpressionOperator Operator { get; set; }
    //-------------
    public string Value { get; set; }
    //----------
    public bool IsCaseSensitive { get; set; }
  }

  #region -- enum -----------------
  // Summary:
  //     Operator used in FilterDescriptor class
  public enum FilterDescriptorExpressionOperator
  {
    // Summary:
    //     Left operand must be smaller than the right one
    IsLessThan = 0,
    //
    // Summary:
    //     Left operand must be smaller than or equal to the right one
    IsLessThanOrEqualTo = 1,
    //
    // Summary:
    //     Left operand must be equal to the right one
    IsEqualTo = 2,
    //
    // Summary:
    //     Left operand must be different from the right one
    IsNotEqualTo = 3,
    //
    // Summary:
    //     Left operand must be larger than the right one
    IsGreaterThanOrEqualTo = 4,
    //
    // Summary:
    //     Left operand must be larger than or equal to the right one
    IsGreaterThan = 5,
    //
    // Summary:
    //     Left operand must start with the right one
    StartsWith = 6,
    //
    // Summary:
    //     Left operand must end with the right one
    EndsWith = 7,
    //
    // Summary:
    //     Left operand must contain the right one
    Contains = 8,
    //
    // Summary:
    //     Left operand must be contained in the right one
    IsContainedIn = 9,
  }

  // Summary:
  //     Enumeration of logical operators for filter collections
  public enum FilterDescriptorLogicalOperator
  {
    // Summary:
    //     Filters are AND'ed
    And = 0,
    //
    // Summary:
    //     Filters are OR'ed
    Or = 1,
  }

  #endregion
}
